package pl.edu.agh.pros.internal.proxy.service;

import java.util.UUID;

import org.jivesoftware.smack.packet.Message;
import org.ros.exception.ServiceException;
import org.ros.namespace.GraphName;
import org.ros.node.ConnectedNode;
import org.ros.node.service.ServiceResponseBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import pl.edu.agh.pros.conf.ProxyConfiguration;
import pl.edu.agh.pros.internal.proxy.AbstractProxyNode;
import pl.edu.agh.pros.internal.proxy.IMessageReceiver;
import pl.edu.agh.pros.internal.proxy.ProxyDispatcher;
import pl.edu.agh.pros.prop.Props;
import pl.edu.agh.pros.util.RosServiceUtils;
import pl.edu.agh.pros.util.ServiceObjectLatch;

public class ProxyServiceSender extends AbstractProxyNode implements IMessageReceiver {

	private static final Logger logger = LoggerFactory.getLogger(ProxyServiceSender.class);
	private final ServiceResponseManager<org.ros.internal.message.Message> serviceResponseManager = new ServiceResponseManager<org.ros.internal.message.Message>();
	private final String serverDevId;

	public ProxyServiceSender(ProxyDispatcher proxyDispatcher, ProxyConfiguration proxyNodeConfiguration,
			String serverDevId) {
		super(proxyDispatcher, proxyNodeConfiguration);
		this.serverDevId = serverDevId;
	}

	@Override
	public GraphName getDefaultNodeName() {
		return new GraphName("pros_core/proxyServiceSender"); //$NON-NLS-1$
	}

	@Override
	public void onStart(ConnectedNode node) {
		this.node = node;
		final String serverXmppAddress = proxyDispatcher.getXmppAddressForDevice(serverDevId);
		if (serverXmppAddress == null) {
			logger.error("Could not resolve ServiceServer xmppAddress"); //$NON-NLS-1$
			node.shutdown();
			return;
		}
		node.newServiceServer(proxyNodeConfiguration.getName(), proxyNodeConfiguration.getType(),
				new ServiceResponseBuilder<org.ros.internal.message.Message, org.ros.internal.message.Message>() {
					@Override
					public void build(org.ros.internal.message.Message request,
							org.ros.internal.message.Message response) throws ServiceException {
						UUID uuid = serviceResponseManager.generateObjectId();
						ServiceObjectLatch<org.ros.internal.message.Message> futureObject = serviceResponseManager
								.createFutureObject(uuid);
						sendServiceRequest(request, uuid, serverXmppAddress);
						try {
							response = futureObject.get();
							if (response == null) {
								throw futureObject.getException();
							}
						} catch (InterruptedException e) {
							logger.error("An error occured while waiting for service response", e); //$NON-NLS-1$
						}
					}
				});
		proxyDispatcher.registerMessageReceiver(proxyNodeConfiguration.getType(), this);
	}
	
	@Override
	public void onMessage(Message message) {
		UUID uuid = (UUID) message.getProperty(Props.XMPP_MSG_UUID);
		if (message.getProperty(Props.XMPP_MSG_VALUE) == null) {
			serviceResponseManager.onExceptionArrived(uuid,
					(ServiceException) message.getProperty(Props.XMPP_MSG_EXCEPTION));
		} else {
			org.ros.internal.message.Message response = RosServiceUtils.deserializeServiceResponse(message, node,
					proxyNodeConfiguration.getType());
			serviceResponseManager.onResponseArrived(uuid, response);
		}
	}

	@Override
	public boolean isRunning() {
		return this.node != null;
	}

	@Override
	public void dispose() {
		proxyDispatcher.unregisterMessageReceiver(proxyNodeConfiguration.getType());
	}

	private void sendServiceRequest(org.ros.internal.message.Message request, UUID uuid, String serverXmppAddress) {
		Message xmppMessage = RosServiceUtils.serializeServiceRequest(request, this.node,
				proxyNodeConfiguration.getType());
		xmppMessage.setProperty(Props.XMPP_MSG_UUID, uuid);
		proxyDispatcher.sendXmppMessage(xmppMessage, serverXmppAddress);
	}

}
